package com.easycoding.framework.repository.support.query;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.jpa.domain.Specification;



/**
 * QueryInfo查询条件
 * @author Wangjk
 * @Date 2013-9-25
 * @Version 1.0
 * @param <T>
 */
public class QueryInfoSpecification<T> implements Specification<T>{
	
	private static final Log log = LogFactory.getLog(QueryInfoSpecification.class);
	
	private QueryInfo<T> queryInfo;
	
	/**查询规则**/
	private List<Predicate> predicateList;

	
	
	public QueryInfoSpecification(QueryInfo<T> queryInfo){
		this.queryInfo = queryInfo;
		this.predicateList = new ArrayList<Predicate>() ;
	}

	@Override
	public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
		if(queryInfo !=null ){
			if(predicateList.isEmpty()){//是否已转化为查询条件
				Long startTime = System.currentTimeMillis();
				List<Criteria> criterias = queryInfo.getCriterias(); 
				if(criterias != null && criterias.size()>0){
					if(log.isDebugEnabled())
						log.debug("查询规则个数:"+criterias.size());
					for(Criteria criteria : criterias){
						Predicate condition = this.buildPredicate(root,cb,criteria);
						if(condition != null)
							predicateList.add(condition);
					}
				}
				if(log.isDebugEnabled()){
					Long endTime = System.currentTimeMillis();
					log.debug("转化查询规则耗时:" + (endTime - startTime) + "ms");
				}
			}
			if(predicateList != null && !predicateList.isEmpty()){
				 Predicate[] predicates = new Predicate[predicateList.size()];
	             predicateList.toArray(predicates);
				 query.where(predicates);
			}
		}
		return null;
	}

	/**
	 * 创建单个查询条件
	 * @param root
	 * @param cb
	 * @param param
	 * @return
	 * @throws NoSuchMethodException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	private Predicate buildPredicate(Root<T> root, CriteriaBuilder cb, Criteria criteria){
		Predicate p = null;
		try {
			Criteria[] children = criteria.getChildren();
			/**子查询*/
			if(children != null && children.length >0){
				if(log.isDebugEnabled()){
					log.debug("子查询----" + "查询类型：" + criteria.getQueryType().toString() + "-----子查询规则数量：" + children.length);
					Predicate[] childPredicate = new Predicate[children.length];
					for(int i=0 ; i < children.length ; i++){
						childPredicate[i] = this.buildPredicate(root, cb, children[i]);
					}
					Class<?> c = cb.getClass();
					Method method = c.getMethod(criteria.getQueryType().toString(),Predicate[].class);
					p = (Predicate)method.invoke(cb, (Object)childPredicate);
				}
			}
			else{
				if(log.isDebugEnabled()){
					log.debug("转换查询规则--------------");
					if(criteria.getKey() == null)
						log.debug("key为空");
					List<QueryExpression<?>> values = criteria.getValues();
					if(values == null || values.isEmpty())
						log.debug("values为空");
				}
				if(criteria.getKey() != null ){
					log.debug("匹配类型:"+criteria.getMatchType().toString());
					if(log.isDebugEnabled())
						log.debug("key:");
					Expression<?> key = criteria.getKey().expression(root, cb);
					if(log.isDebugEnabled())
						log.debug("values:");
					Expression<?>[] values = this.buildValueExpression(root, cb,criteria);
					/**in*/
					if(criteria.getMatchType().equals(MatchType.in)){
						if(values != null && values.length > 0){
							p = key.in(values);
						}
					}
					else{
						Object[] args = new Object[1 + (values != null ? values.length : 0)] ;
						Class<?>[] parameterTypes = new Class<?>[1 + (values != null ? values.length : 0)];
						args[0] = key;
						parameterTypes[0] = Expression.class;
						if(values != null && values.length > 0){
							for(int i = 0 ;i < values.length ; i++){
								if(values[i] instanceof Expression){
									parameterTypes[i+1] = Expression.class;
								}
								else
									parameterTypes[i+1] = Object.class;
								args[i+1] = values[i];
							}
						}
						Class<?> c = cb.getClass();
						Method method = c.getMethod(criteria.getMatchType().toString(), parameterTypes);
						p = (Predicate)method.invoke(cb, args);
					}
					if(criteria.isNot()){
						p.not();
					}
				}
			}
		} catch (NoSuchMethodException e) {
			log.error("转换查询规则反射异常",e);
		} catch (IllegalAccessException e) {
			log.error("转换查询规则反射异常",e);
		} catch (InvocationTargetException e) {
			log.error("转换查询规则反射异常",e);
		}
		return p;
	}
	
	/**
	 * 转换表达式
	 * @param root
	 * @param cb
	 * @param criteria
	 * @return
	 */
	private Expression<?>[] buildValueExpression(Root<T> root, CriteriaBuilder cb , Criteria criteria){
		if(criteria != null && criteria.getValues() != null && criteria.getValues().size()>0){
			Expression<?>[] args = new Expression<?>[criteria.getValues().size()];
			int i = 0;
			for(Object e : criteria.getValues()){
				if (e instanceof QueryExpression) 
					args[i] = ((QueryExpression<?>)e).expression(root, cb);
				i ++ ;
			}
			return args;
		}
		return null;
	}
}
